﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SQLite;

namespace SQLiteDictionary
{
    public class SqliteDictionary<TKey, TValue> : IEnumerable<SqliteKeyValuePar<TKey, TValue>>
        where TKey : IEquatable<TKey>
    {
        SQLiteAsyncConnection _cn;

        public SqliteDictionary(SQLiteDictionaryStorage StorageFolder, string DataBaseName)
        {
            INIDictionary(StorageLocal.GetPath(StorageFolder, DataBaseName));
        }
        public SqliteDictionary(string SavingLocation)
        {
            INIDictionary(SavingLocation);
        }
        public SqliteDictionary()
        {
            INIDictionary("MySqliteDictionary");
        }

        private void INIDictionary(string LOCAL)
        {
            _cn = new SQLiteAsyncConnection(LOCAL);
            _cn.CreateTableAsync<SqliteKeyValuePar<TKey, TValue>>();
        }

        /// <summary>
        /// Check if Key exist
        /// </summary>
        /// <param name="KEY">Key value</param>
        /// <returns></returns>
        public bool ContainsKey(TKey Key)
        {
            return _cn.Table<SqliteKeyValuePar<TKey, TValue>>().Where(z => z.Key.Equals(Key)).CountAsync().Result > 0;
        }
        /// <summary>
        /// If 'Key' didn't exist in database one is created with 'Value'
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Value"></param>
        /// <returns>True if a new key is created</returns>
        public bool Create(TKey Key, TValue Value)
        {
            if (ContainsKey(Key))
                return false;

            Set(Key, Value);
            return true;
        }
        /// <summary>
        /// If Key didn't exit one is create with 'value', if already exist the value is updated
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Value"></param>
        public void Set(TKey Key, TValue Value)
        {
            SqliteKeyValuePar<TKey, TValue> par = new SqliteKeyValuePar<TKey, TValue>() { Key = Key, Value = Value };

            if (ContainsKey(Key))
                _cn.UpdateAsync(par);
            else
                _cn.InsertAsync(par);
        }
        /// <summary>
        /// Remove key if exist
        /// </summary>
        /// <param name="Key"></param>
        /// <returns></returns>
        public bool Remove(TKey Key)
        {
            if (ContainsKey(Key))
            {
                SqliteKeyValuePar<TKey, TValue> par = new SqliteKeyValuePar<TKey, TValue>() { Key = Key, Value = this.Get(Key) };
                _cn.DeleteAsync(par);
                return true;
            }

            return false;
        }
        /// <summary>
        /// Return value of Key
        /// </summary>
        /// <param name="Key"></param>
        /// <returns></returns>
        public TValue Get(TKey Key)
        {
            if (ContainsKey(Key))
                return _cn.Table<SqliteKeyValuePar<TKey, TValue>>().Where(z => z.Key.Equals(Key)).FirstAsync().Result.Value;

            throw new Exception("Invalid Key");
        }

        public TValue this[TKey Key]
        {
            get { return Get(Key); }
            set { Set(Key, value); }
        }

        public IEnumerable<TKey> Keys
        {
            get { return new List<TKey>(_cn.Table<SqliteKeyValuePar<TKey, TValue>>().ToListAsync().Result.Select(z => z.Key)); }
        }
        public IEnumerable<TValue> Values
        {
            get { return new List<TValue>(_cn.Table<SqliteKeyValuePar<TKey, TValue>>().ToListAsync().Result.Select(z => z.Value)); }
        }

        public IEnumerator<SqliteKeyValuePar<TKey, TValue>> GetEnumerator()
        {
            return new SqliteKeyValueParEnumerator<TKey, TValue>(_cn.Table<SqliteKeyValuePar<TKey, TValue>>().ToListAsync().Result);
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }

    public class SqliteDictionary<TValue> : IEnumerable<SqliteKeyValuePar<string, TValue>>
    {
        SQLiteAsyncConnection _cn;

        public SqliteDictionary(SQLiteDictionaryStorage StorageFolder, string DataBaseName)
        {
            INIDictionary(StorageLocal.GetPath(StorageFolder, DataBaseName));
        }
        public SqliteDictionary(string SavingLocation)
        {
            INIDictionary(SavingLocation);
        }
        public SqliteDictionary()
        {
            INIDictionary("MySqliteDictionary");
        }

        private void INIDictionary(string LOCAL)
        {
            _cn = new SQLiteAsyncConnection(LOCAL);
            _cn.CreateTableAsync<SqliteKeyValuePar<string, TValue>>();
        }

        public bool ContainsKey(string Key)
        {
            return _cn.Table<SqliteKeyValuePar<string, TValue>>().Where(z => z.Key.Equals(Key)).CountAsync().Result > 0;
        }
        public void Set(string Key, TValue Value)
        {
            SqliteKeyValuePar<string, TValue> par = new SqliteKeyValuePar<string, TValue>() { Key = Key, Value = Value };

            if (ContainsKey(Key))
                _cn.UpdateAsync(par);
            else
                _cn.InsertAsync(par);
        }
        public bool Create(string Key, TValue Value)
        {
            if (ContainsKey(Key))
                return false;

            Set(Key, Value);
            return true;
        }
        public bool Remove(string Key)
        {
            if (ContainsKey(Key))
            {
                SqliteKeyValuePar<string, TValue> par = new SqliteKeyValuePar<string, TValue>() { Key = Key, Value = this.Get(Key) };
                _cn.DeleteAsync(par);
                return true;
            }

            return false;
        }
        public TValue Get(string Key)
        {
            if (ContainsKey(Key))
                return _cn.Table<SqliteKeyValuePar<string, TValue>>().Where(z => z.Key.Equals(Key)).FirstAsync().Result.Value;

            throw new Exception("Invalid Key");
        }

        public TValue this[string Key]
        {
            get { return Get(Key); }
            set { Set(Key, value); }
        }

        public IEnumerable<string> Keys
        {
            get { return new List<string>(_cn.Table<SqliteKeyValuePar<string, TValue>>().ToListAsync().Result.Select(z => z.Key)); }
        }
        public IEnumerable<TValue> Values
        {
            get { return new List<TValue>(_cn.Table<SqliteKeyValuePar<string, TValue>>().ToListAsync().Result.Select(z => z.Value)); }
        }

        public IEnumerator<SqliteKeyValuePar<string, TValue>> GetEnumerator()
        {
            return new SqliteKeyValueParEnumerator<string, TValue>(_cn.Table<SqliteKeyValuePar<string, TValue>>().ToListAsync().Result);
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
}
